﻿using System;
using System.Collections;
using System.Collections.Generic;

namespace JH
{
    /// <summary>
    /// 固定长度的数组
    /// </summary>
    public class FixedArray<T> : IFixedContainer<T>
    {
        /// <summary>
        ///  溢出模式
        /// </summary>
        public OverflowMode Mode { get; }

        /// <summary>
        /// 这个值指向数组实际存放的首个元素的位置,数组为空时为 -1
        /// </summary>
        private int _header;

        /// <summary>
        /// 这个值指向数组实际存放的最后一个元素的位置，数组为空时为-1
        /// </summary>
        private int _tail;

        /// <summary>
        ///  栈的最大容量
        /// </summary>
        public int MaxSize { get; }

        /// <summary>
        ///  使用数据进行内部存储
        /// </summary>
        private readonly T[] _array;

        /// <summary>
        ///  当元素因为存储容量满了，添加新元素而被丢弃时触发
        /// </summary>
        public event Action<T> OnItemDiscard;

        /// <summary>
        ///  当前元素数量
        /// </summary>
        public int Count
        {
            get
            {
                if (_header == -1 && _tail == -1) return 0;

                return _tail < _header ? _tail + MaxSize - _header + 1 : _tail - _header + 1;
            }
        }

        /// <summary>
        ///  是否只读
        /// </summary>
        public bool IsReadOnly => false;

        /// <summary>
        /// 构造函数，设置固定大小的栈的最大容量
        /// </summary>
        public FixedArray(int maxSize, OverflowMode mode = OverflowMode.DiscardOldestAdd)
        {
            if (maxSize <= 0)
                throw new ArgumentException("maxSize 必须为正数", nameof(maxSize));

            // 设置最大容量
            MaxSize = maxSize;

            // 设置溢出模式
            Mode = mode;

            // 初始化数组
            _array = new T[MaxSize];

            // 初始化指针
            _header = _tail = -1;
        }
        
        /// <summary>
        ///  尝试按照数组为空的逻辑添加，失败则表示数组不为空，返回false
        /// </summary>
        private bool TryAddAsEmpty(T item)
        {
            if (Count != 0) return false;

            // 头指针指向首个元素位置，尾指针指向最后一个元素位置，都是0
            _header = _tail = 0;

            // 将新元素放在头部和尾部
            _array[_header] = item;

            return true;
        }

        /// <summary>
        /// 添加一个元素
        /// </summary>
        public void Add(T item)
        {
            //处理容量溢出
            this.HandleOverflow();

            // 如果容器不为空
            if (!TryAddAsEmpty(item))
            {
                //如果容器不为空时，尾指针向后移动一位
                _tail = (_tail + 1) % MaxSize;

                // 将新元素放在尾部
                _array[_tail] = item;
            }
        }

        /// <summary>
        ///  移除一个元素
        /// </summary>
        public bool Remove(T item)
        {
            var index = FindIndex(item);
            if (index == -1) return false;

            // 移除元素
            RemoveAt(index);

            return true;
        }

        /// <summary>
        /// 寻找元素的索引
        /// </summary>
        public int FindIndex(T item, IEqualityComparer<T> comparer = null)
        {
            for (int i = 0; i < Count; i++)
            {
                comparer ??= EqualityComparer<T>.Default;
                if (comparer.Equals(_array[(_header + i) % MaxSize], item))
                    return i;
            }

            return -1;
        }

        /// <summary>
        ///  获取元素的索引
        /// </summary>
        public int IndexOf(T item)
        {
            return FindIndex(item);
        }

        /// <summary>
        ///  在指定索引处插入一个元素
        /// </summary>
        public void Insert(int index, T item)
        {
            // 检查索引范围
            if (index < 0 || index > Count)
                throw new ArgumentOutOfRangeException($"索引超出范围，当前仅 {Count} 个元素！企图在索引 {index} 处插入元素！");

            // 处理容量溢出
            this.HandleOverflow();

            // 插入头部的情况
            if (index == 0)
            {
                // 如果数组不为空
                if (!TryAddAsEmpty(item))
                {
                    // 头指针向前移动一位
                    _header = (_header - 1 + MaxSize) % MaxSize;
                    _array[_header] = item;
                }
            }
            // 插入尾部的情况
            else if (index == Count + 1)
            {
                // 尾指针向后移动一位
                _tail = (_tail + 1) % MaxSize;
                _array[_tail] = item;
            }
            // 插入中间的情况
            else
            {
                // 计算插入位置
                var position = (_header + index) % MaxSize;

                //保证插入位置>头指针
                while (position < _header) position += MaxSize;

                //保证尾指针在头指针之后
                while (_tail < _header) _tail += MaxSize;

                // 从插入位置到尾部的元素都向后移动一位
                for (int i = _tail; i >= position; i--)
                {
                    //从尾部开始移动元素
                    _array[(i + 1) % MaxSize] = _array[i % MaxSize];
                }

                // 插入元素到指定位置
                _array[position % MaxSize] = item;

                // 更新尾指针
                _tail = (_tail + 1) % MaxSize;
            }
        }

        /// <summary>
        ///  移除一个元素
        /// </summary>
        public void RemoveAt(int index)
        {
            if (index < 0 || index >= Count)
                throw new ArgumentOutOfRangeException("索引超出范围，当前仅" + Count + "个元素！");

            //如果只有一个元素，直接清空数组
            if (Count == 1)
            {
                _array[_header] = default; // 清除引用
                _header = _tail = -1; // 重置为空
                return;
            }

            //移除头部元素
            if (index == 0)
            {
                _array[_header] = default; // 清除引用
                _header = (_header + 1) % MaxSize; //头指针向后移动一位
            }
            //移除尾部元素
            else if (index == Count - 1)
            {
                _array[_tail] = default; // 清除引用
                _tail = (_tail - 1 + MaxSize) % MaxSize; //尾指针向前移动一位
            }

            //移除中间元素
            else
            {
                var position = (_header + index) % MaxSize;

                // 保证插入位置大于头指针
                while (position < _header) position += MaxSize;

                // 保证尾指针在头指针之后
                while (_tail < _header) _tail += MaxSize;

                // 从当前位置到尾指针，逐个向前移动
                for (int i = position; i <= _tail; i++)
                {
                    //将后一个元素覆盖前一个元素
                    _array[i % MaxSize] = _array[(i + 1) % MaxSize];
                }

                // 尾指针向前移动一位
                _tail = (_tail - 1 + MaxSize) % MaxSize;
            }
        }

        /// <summary>
        ///   是否包含某个元素
        /// </summary>
        public bool Contains(T item)
        {
            return FindIndex(item) != -1;
        }

        /// <summary>
        ///  清空栈
        /// </summary>
        public void Clear()
        {
            //将数组中的所有元素置为默认值,不再持有任何元素的引用
            for (int i = 0; i < Count; i++) this[i] = default;

            //重置指针
            _header = _tail = -1;
        }

        /// <summary>
        ///  拷贝到数组
        /// </summary>
        /// <param name="target">目标数组</param>
        /// <param name="index">从目标数组的哪个位置开始拷贝</param>
        public void CopyTo(T[] target, int index)
        {
            if (target == null)
                throw new ArgumentNullException(nameof(target), "目标数组不能为空");
            if (index < 0)
                throw new ArgumentOutOfRangeException(nameof(index), "索引不合法，小于0的索引是无效的！当前传入：" + index);
            if (target.Length - index < Count)
                throw new ArgumentException("目标数组空间不足");

            for (int i = 0; i < Count; i++)
            {
                target[index + i] = this[i];
            }
        }

        public IEnumerator<T> GetEnumerator()
        {
            // 如果没有元素，直接返回
            if (Count == 0) yield break;

            for (int i = 0; i < Count; i++)
            {
                var currentIndex = (_header + i) % MaxSize;
                yield return _array[currentIndex];
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public T this[int index]
        {
            get
            {
                if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException("索引超出范围，当前仅" + Count + "个元素！");

                return _array[(_header + index) % MaxSize];
            }
            set
            {
                if (index < 0 || index >= Count)
                    throw new ArgumentOutOfRangeException(nameof(index), $"索引超出范围，当前仅{Count}个元素！");

                _array[(_header + index) % MaxSize] = value;
            }
        }

        /// <summary>
        ///  根据溢出行为枚举，获取需要丢弃的元素
        /// </summary>
        T IFixedContainer<T>.GetDiscardItem()
        {
            //对于当前集合，丢弃首个元素，和丢弃最早添加的元素是等价的
            //丢弃末尾元素，和丢弃最新添加的元素是等价的
            // 是否是丢弃首个元素
            bool isFirstDiscard = Mode is OverflowMode.DiscardFirst or OverflowMode.DiscardOldestAdd;

            // 根据溢出模式，决定丢弃哪个元素
            return  isFirstDiscard ? _array[_header] : _array[_tail];
        }

        /// <summary>
        ///  引发丢弃事件
        /// </summary>
        void IFixedContainer<T>.InvokeEvent(T item)
        {
            OnItemDiscard?.Invoke(item);
        }
     
    }
}